#include "mainalgo.h"
#include "ui_mainalgo.h"

MainAlgo::MainAlgo(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainAlgo), solution(), Pieces(), correspondanceCI(), correspondanceIC()
{
    ui->setupUi(this);
    qApp->setApplicationName("Nom");
    this->patchJeton = "/home/buse974/Documents/IMERIR/qtAlgo/jeton";
    this->patchSortieJeton = "/home/buse974/Documents/IMERIR/qtAlgo/Sortiejeton";
    this->tailleMax=7;
    this->init();
    this->stopAlgo = false;
    qsrand(QDateTime::currentDateTime ().toTime_t ());
    this->useListTabou = false;
    this->tailleListeTabou = 1;
}

// Destructeur
MainAlgo::~MainAlgo()
{
    delete ui;
}

void MainAlgo::init()
{
    this->correspondanceCI.insert('N',0);
    this->correspondanceCI.insert('E',1);
    this->correspondanceCI.insert('S',2);
    this->correspondanceCI.insert('O',3);

    this->correspondanceIC.insert(-2,'S');
    this->correspondanceIC.insert(-1,'O');
    this->correspondanceIC.insert(0,'N');
    this->correspondanceIC.insert(1,'E');
    this->correspondanceIC.insert(2,'S');
    this->correspondanceIC.insert(3,'O');
    this->correspondanceIC.insert(4,'N');
    this->correspondanceIC.insert(5,'E');

    QFile file(this->patchJeton);
    QStringList list;
    file.open(QIODevice::ReadOnly | QIODevice::Text);
    QTextStream flux(&file);

    for(int i=0;i<16;i++) {
        QVector<Pion> lignePion;
        for(int x=0;x<16;x++)
        {
            list = flux.readLine().simplified().split(",");
            QVector<char> alpha;
                alpha.append(list.at(1).toAscii()[0]);
                alpha.append(list.at(2).toAscii()[0]);
                alpha.append(list.at(3).toAscii()[0]);
                alpha.append(list.at(4).toAscii()[0]);
           this->Pieces.append(alpha);
           Pion p;
           p.nord = 'N';
           p.num = list.at(0).toInt()-1;
           lignePion.append(p);
        }
        this->solution.append(lignePion);
    }

    for(int i=0;i<16;i++)
        for(int x=0;x<16;x++)
            qDebug() <<" num: "<< this->solution.at(i).at(x).num<<" nord: "<< this->solution.at(i).at(x).nord;

 //   this->initialisationSolution();
}

int MainAlgo::checker(QVector<QVector<Pion> > &sol)
{
    int score=0;
    for(int x=0;x<16;x++)
        for(int y=0;y<16;y++){
            if((x-1)>=0)
            {
                char n = this->pole(sol.at(x).at(y).num,sol.at(x).at(y).nord,'N');
                char s = this->pole(sol.at(x-1).at(y).num,sol.at(x-1).at(y).nord,'S');

                if(n==s && n!= 'Z' && s!='Z'){
                    score++;

                }
            }
            if((y+1)<16)
            {
                char e = this->pole(sol.at(x).at(y).num,sol.at(x).at(y).nord,'E') ;
                char o = this->pole(sol.at(x).at(y+1).num,sol.at(x).at(y+1).nord,'O');


                if(e==o && e!='Z' && o!='Z'){
                    score++;
                }
            }
    }
    return score;
}

char MainAlgo::pole(int num, char ori, char p)
{
  int tour = this->correspondanceCI.value(p)+this->correspondanceCI.value(ori);

  if(tour>3) tour-=4;
    return this->Pieces.at(num).at(tour);
}

void MainAlgo::vvpToVvpp(QVector<QVector<Pion> > &in,QVector<QVector<PionPos> > &out)
{
    out.clear();

    // ensemble coin
    QVector<PionPos> ensembleCoin;
    PionPos angle1,angle2,angle3,angle4;

    angle1.piece = in.at(0).at(0);
    angle1.x = 0;  angle1.y = 0;
    angle2.piece = in.at(15).at(15);
    angle2.x = 15; angle2.y = 15;
    angle3.piece = in.at(15).at(0);
    angle3.x = 15; angle3.y = 0;
    angle4.piece = in.at(0).at(15);
    angle4.x = 0;  angle4.y = 15;

    ensembleCoin.append(angle1);
    ensembleCoin.append(angle2);
    ensembleCoin.append(angle3);
    ensembleCoin.append(angle4);

    // ensemble cote
    QVector<PionPos> ensembleCote;
    for(int i=1;i<15;i++)
    {
        PionPos cote,cote2,cote3,cote4;
        cote.piece = in.at(0).at(i);
        cote.x = 0;  cote.y = i;
        cote2.piece = in.at(15).at(i);
        cote2.x = 15;  cote2.y = i;
        cote3.piece = in.at(i).at(0);
        cote3.x = i;  cote3.y = 0;
        cote4.piece = in.at(i).at(15);
        cote4.x = i;  cote4.y = 15;

        ensembleCote.append(cote);
        ensembleCote.append(cote2);
        ensembleCote.append(cote3);
        ensembleCote.append(cote4);
    }

    // ensemble centre
    QVector<PionPos> ensembleCentre;
    for(int x=1;x<15;x++)
        for(int y=1;y<15;y++)
        {
            PionPos centre;
            centre.piece = in.at(x).at(y);
            centre.x = x;  centre.y = y;
            ensembleCentre.append(centre);
        }

    out.append(ensembleCoin);
    out.append(ensembleCote);
    out.append(ensembleCentre);
}

void MainAlgo::BonChangement(QVector<QVector<Pion> > &in,int t,int nb)
{
    QVector<QVector<PionPos> > out;
    vvpToVvpp(in,out);

    for(int i=0;i<nb;i++){
        this->Permute(out[t]);
        if(t==2) { this->rotate(out[t]); }
    }
    vvppToVvp(out,in);
}

void MainAlgo::Permute(QVector<PionPos> &pion)
{
    bool change=true;

   int i=0,j=0,posX,posY;

   do{
        change=true;

         i = qrand()%pion.size();
         j = qrand()%pion.size();

         if(this->useListTabou)
             for(int x=0;x<this->listTabou.size();x++)
                 if( (this->listTabou.at(x).pion1.num==pion[i].piece.num && this->listTabou.at(x).pion2.num==pion[j].piece.num) || (this->listTabou.at(x).pion1.num==pion[j].piece.num && this->listTabou.at(x).pion2.num==pion[i].piece.num) )
                     change = false;
   }
   while(i==j && change==false);


   // Permutation
   posX = pion.at(i).x;
   posY = pion.at(i).y;
   pion[i].x = pion[j].x;
   pion[i].y = pion[j].y;
   pion[j].x = posX;
   pion[j].y = posY;
   this->BonOrientation(pion[i]);
   this->BonOrientation(pion[j]);

   if(this->useListTabou){
       if(this->listTabou.size()>=this->tailleListeTabou)
           this->listTabou.remove(0);

           PairePionPos pp;
           pp.pion1 = pion[i].piece;
           pp.pion2 = pion[j].piece;
           this->listTabou.append(pp);
   }
}

void MainAlgo::rotate(QVector<PionPos> &pion)
{
    bool change = true;
    int cardinaux,x_pos;

    do{
           change=true;
           x_pos= qrand()%pion.size();

           if(this->useListTabou)
               for(int x=0;x<this->listTabouR.size();x++)
                   if(this->listTabouR.at(x) == x_pos)
                       change = false;
    }while(change==false);

   do{  cardinaux= qrand()%4; } while(pion[x_pos].piece.nord==this->correspondanceIC.value(cardinaux));

   if(this->useListTabou){
       if(this->listTabouR.size()>=this->tailleListeTabou)
           this->listTabouR.remove(0);
           this->listTabouR.append(pion[x_pos].piece.num);
   }

   pion[x_pos].piece.nord =this->correspondanceIC.value(cardinaux);
}

int MainAlgo::searchLastZ(QVector<char> z){
    int num= -1;
    bool premierIsZ = (z.at(0)=='Z') ? true : false;

    for(int i=0;i<z.size(); i++)
        if(z.at(i) == 'Z')
            num = i;

    if(premierIsZ && num==3) return 0;
    else return num;
}

void MainAlgo::BonOrientation(PionPos &pp)
{
       if(pp.x==0 && pp.y!=0 && pp.y!=15) pp.piece.nord = this->correspondanceIC.value(this->searchLastZ(this->Pieces.at(pp.piece.num))+1);  // SI COTE GAUCHE
       if(pp.x==15 && pp.y!=0 && pp.y!=15) pp.piece.nord = this->correspondanceIC.value(this->searchLastZ(this->Pieces.at(pp.piece.num))-1); // SI COTE DROIT
       if(pp.y==0 && pp.x!=0 && pp.x!=15) pp.piece.nord = this->correspondanceIC.value(this->searchLastZ(this->Pieces.at(pp.piece.num)));    // SI COTE HAUT
       if(pp.y==15 && pp.x!=0 && pp.x!=15) pp.piece.nord = this->correspondanceIC.value(this->searchLastZ(this->Pieces.at(pp.piece.num))+2); // SI COTE BAS
       if(pp.x==0 && pp.y==0) pp.piece.nord = this->correspondanceIC.value(this->searchLastZ(this->Pieces.at(pp.piece.num)));
       if(pp.x==0 && pp.y==15)pp.piece.nord = this->correspondanceIC.value(this->searchLastZ(this->Pieces.at(pp.piece.num))+1);
       if(pp.x==15 && pp.y==0) pp.piece.nord = this->correspondanceIC.value(this->searchLastZ(this->Pieces.at(pp.piece.num))-1);
       if(pp.x==15 && pp.y==15) pp.piece.nord  = this->correspondanceIC.value(this->searchLastZ(this->Pieces.at(pp.piece.num))+2);
}

// Pour savoir a quel ensemble la piece appartient
int MainAlgo::nbdeZ(QVector<char> z)
{
    int cpt = 0;
    for(int i=0;i<z.size(); i++)
        if(z.at(i) == 'Z')
            cpt++;
    return cpt;
}

// Initialisation d'une premiere solution
void MainAlgo::initialisationSolution()
{
    int cpt=0;
    QVector<PionPos> ensembleCoin;          // Ensemble des coins
    QVector<PionPos> ensembleCentre;        // Ensemble central
    QVector<PionPos> ensembleCote;          // Ensemble des cotés
    QVector<QVector<PionPos> > ensemble;


    // Parcours du vecteur de piece
    for(int i=0; i<this->Pieces.size(); i++){
        int nb = this->nbdeZ(this->Pieces.at(i));
        PionPos tmp;

        tmp.piece = Pion();
        tmp.piece.num = i;

        switch(nb){
            case 1:
                ensembleCote.append(tmp);
            break;

            case 2:
                ensembleCoin.append(tmp);
            break;

            default:
                ensembleCentre.append(tmp);
            break;
        }
    }

        // ==================  Position des pieces
        // Pour les pieces des coins
        ensembleCoin[0].x = 0;
        ensembleCoin[0].y = 0;
        ensembleCoin[1].x = 15;
        ensembleCoin[1].y = 15;
        ensembleCoin[2].x = 15;
        ensembleCoin[2].y = 0;
        ensembleCoin[3].x = 0;
        ensembleCoin[3].y = 15;

        this->BonOrientation(ensembleCoin[0]);
        this->BonOrientation(ensembleCoin[2]);
        this->BonOrientation(ensembleCoin[1]);
        this->BonOrientation(ensembleCoin[3]);

       // Ensemble des cots
        for(int i=1; i<15; i++){
            ensembleCote[i-1].x = 0;
            ensembleCote[i-1].y = i;
            ensembleCote[i-1+14].x = i;
            ensembleCote[i-1+14].y = 0;
            ensembleCote[i-1+28].x = i;
            ensembleCote[i-1+28].y = 15;
            ensembleCote[i-1+42].x = 15;
            ensembleCote[i-1+42].y = i;

            this->BonOrientation(ensembleCote[i-1]);
            this->BonOrientation(ensembleCote[i-1+14]);
            this->BonOrientation(ensembleCote[i-1+28]);
            this->BonOrientation(ensembleCote[i-1+42]);
        }

        for(int i=1; i<15; i++)
            for(int j=1; j<15; j++){
                ensembleCentre[cpt].piece.nord = 'E';
                ensembleCentre[cpt].x = i;
                ensembleCentre[cpt++].y = j;
            }

        ensemble.append(ensembleCoin);
        ensemble.append(ensembleCote);
        ensemble.append(ensembleCentre);

    this->vvppToVvp(ensemble,this->solution);
}

void MainAlgo::on_check_clicked()
{
   // this->BonChangement(this->solution,2,1);
    qDebug() << this->checker(this->solution);
}

void MainAlgo::on_sortie_clicked()
{
    QFile file(this->patchSortieJeton + QString::number(this->checker(this->solution)));
    file.open(QIODevice::ReadWrite | QIODevice::Text);
    QTextStream flux(&file);

    for(int x=0;x<this->solution.size();x++){
        for(int y=0;y<this->solution.size();y++) {
            flux << this->solution.at(x).at(y).num+1;
            flux << ',';
            flux << this->solution.at(x).at(y).nord;
            if(y!=15)  flux << ',';
        }
        flux << endl;
    }
    file.close();
}

void MainAlgo::vvppToVvp(QVector<QVector<PionPos> > &in,QVector<QVector<Pion> > &out)
{
   for(int j=0; j<in.size();j++)
        for(int i=0; i<in.at(j).size();i++)
            out[in[j][i].x][in[j][i].y]=in[j][i].piece;
}

void MainAlgo::on_algo_clicked()
{
    int temp=60;
    qsrand(QDateTime::currentDateTime ().toTime_t ());
    int vMax=5000;
    int type_en_cour = 0;
    int type_val;
    int NBChamgementMax=1;
    int cpt=0;
    int scoreTmp;
    int NBDegradation=1;

    QVector<QVector<Pion> > assignments_changement;

    int scoreNew = this->checker(this->solution);
    QVector<QVector<Pion> > bon_assignments = this->solution;

    int best_score = scoreNew;

     clock_t endwait = clock() + temp * CLOCKS_PER_SEC ;
    //while (!this->stopAlgo)

    while (clock() < endwait-1)
    {
      assignments_changement = bon_assignments;

      type_val = type_en_cour++%3;
      this->BonChangement(assignments_changement,type_val,NBDegradation);

      scoreTmp = this->checker(assignments_changement);

      if(scoreTmp>scoreNew || scoreNew==-1){ // SI AMELIORATION
          scoreNew = scoreTmp;
          bon_assignments = assignments_changement;

          if(best_score < scoreNew){ // SI IL Y A EU DE MEILLEUR RESULTAT ON SAUVEGARDE
                       best_score = scoreNew;
                       this->solution = bon_assignments;
                       NBDegradation=1;
          }
          cpt=0;
      }
      else if(scoreTmp<=scoreNew){ // SI PAS DAMELIORATION
          if(cpt<vMax)  cpt++;
          else { // MINIMUM LOCAL
                  bon_assignments = this->solution;
                  scoreNew =-1;
                  cpt=0;
                  if(NBChamgementMax<=NBDegradation)NBDegradation=1;else NBDegradation++;   // ON REMET LA DEGRADATION A UN
          }
      }
    }

    qDebug() << "FIN";

    this->on_sortie_clicked();
}

void MainAlgo::on_stop_clicked()
{
    this->stopAlgo = true;
}

void MainAlgo::on_descente_clicked()
{
    int temp = 60;
    int type_en_cour = 0;
    int type_val;
    int scoreTmp;

    QVector<QVector<Pion> > assignments_changement;
    int scoreNew = this->checker(this->solution);
    clock_t endwait = clock() + temp * CLOCKS_PER_SEC ;

    while (clock() < endwait-1)
    {
      assignments_changement = this->solution;

      type_val = type_en_cour++%3;
      this->BonChangement(assignments_changement,type_val,1);
      scoreTmp = this->checker(assignments_changement);

      if(scoreTmp>scoreNew)
      {
          qDebug() << scoreTmp;
          scoreNew = scoreTmp;
          this->solution = assignments_changement;
      }

    }
    qDebug() << "FIN";

    this->on_sortie_clicked();
}

void MainAlgo::on_tabou_clicked()
{
    int temp = 10;
    int type_en_cour = 0;
    int type_val;
    int scoreTmp;
    this->useListTabou = true;
    this->tailleListeTabou = 20000;
    QVector<QVector<Pion> > assignments_changement;
    int scoreNew = this->checker(this->solution);
    clock_t endwait = clock() + temp * CLOCKS_PER_SEC ;

    while (clock() < endwait-1)
    {
      assignments_changement = this->solution;

      type_val = type_en_cour++%3;
      this->BonChangement(assignments_changement,type_val,1);



      scoreTmp = this->checker(assignments_changement);

      if(scoreTmp>scoreNew)
      {
          qDebug() << scoreTmp;
          qDebug() <<"LR" << listTabouR.size();
          qDebug() <<"LP" << listTabou.size();
          scoreNew = scoreTmp;
          this->solution = assignments_changement;
      }
      else
      {

      }
    }
    qDebug() << "FIN";

    this->useListTabou = false;
    this->on_sortie_clicked();
}
